<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>SQLite的自动递增</title>
<link rel="stylesheet" type="text/css" href="../css/reset.css" />
<link rel="stylesheet" type="text/css" href="../css/prettify.css" />
<script type="text/javascript" src="../js/prettify.js"></script>
</head>

<body onload="prettyPrint();">
<h1>SQLite的自动递增</h1>
<hr />
<p>在用sqlite设计表时，每个表都有一个自己的整形id值作为主键，其实可以不指定这么一个id值，sqlite内部本来就会为每个表加上一个　rowid，这个rowid可以当成一个隐含的字段使用，但是由sqlite引擎来维护的，在3.0以前rowid是32位的整数，3.0以后是 64位的整数，为什么不直接使用这个内部的rowid作为每个表的id主键呢。</p>
<p>相关的文档在这里：http://www.sqlite.org/autoinc.html?http://www.sqlite.org/faq.html</p>
<p> </p>
<p>用指定INTEGER PRIMARY KEY AUTOINCREMENT 和不指定自增长字段用rowid有什么区别:</p>
<p>使用自增长字段为主键有不少问题，比如维护或是在大型分布应用中主键冲突的解决等。在一些大型分布应用中主键一般选用guid，这可以有效的避免主键冲突，减少对主键维护的工程。当然，对于中小型的应用，自增长字段的好处更多一些，简单、快速。</p>
<p>Sqlite中，一个自增长字段定义为INTEGER PRIMARY KEY AUTOINCREMENT，那么在插入一个新数据时，只需要将这个字段的值指定为NULL，即可由引擎自动设定其值，引擎会设定为最大的rowid+1。当然，也可以设置为非NULL的数字来自己指定这个值，但这样就必须自己小心，不要引起冲突。当这个rowid的值大于所能表达的最大值 9223372036854775807 （3.0及以后版本的rowid最大值）后，rowid的新值会这个最大数之前随机找一个没被使用了的值。所以在rowid达到最大值前，rowid的值是严格单调增加的。<br />
  INTEGER PRIMARY KEY AUTOINCREMENT 自增长字段的算法与rowid稍微有些不同。<br />
  第一，在达到最大值后，rowid会找已被删除的字段对应的rowid作为新值，而自增长字段则会丢出一个SQLITE_FULL的错误。<br />
  第二，自增长字段在增加新值时，是找一个从没被使用过的rowid作为新值，而rowid则是找最大已存在的rowid+1。这里对应用的影响会比较大，尤其是一些对id值有依赖的元记录，只适合使用自增长字段而不能用rowid。<br />
  <br />
  比如，我们设计一个元记录表：<br />
  drop table test;<br />
  create table test (<br />
  [tkid]            integer PRIMARY KEY autoincrement,                -- 设置主键<br />
  [tktype]          int default 0,<br />
  [tableid]         varchar (50),<br />
  [createdate]      datetime default (datetime('now', 'localtime'))    -- 时间<br />
  );</p>
<p><br />
  第三，使用自增长字段，引擎会自动产生一个sqlite_sequence表，用于记录每个表的自增长字段的已使用的最大值，用户可以看到，并可以用使用 Update、Delete和Insert操作，但不建议这么使用，这会让引擎混乱。如果使用rowid，也会有这么一个内部表，用户可以维护rowid 值，但看不到。<br />
  这么看来，如果直接使用rowid来代替自增加字段，根据两者的细微的差别，需要注意是否与自己的应用冲突，如果没有冲突，那么用rowid会更快一点。</p>
<p> </p>
<p>SQLite中创建自增字段:</p>
<p>简单的回答：一个声明为 INTEGER PRIMARY KEY 的字段将自动增加。</p>
<p>从 SQLite 的 2.3.4 版本开始，如果你将一个表中的一个字段声明为 INTEGER PRIMARY KEY，那么无论你何时向该表的该字段插入一个 NULL 值，这个 NULL 值将自动被更换为比表中该字段所有行的最大值大 1 的整数；如果表为空，那么将被更换为 1。</p>
<p>一个新的API函数 sqlite3_last_insert_rowid() 返回最近的插入操作的整形键.</p>
<p>注意这个整型键始终比之前插入表中的最后一个键大1。新键相对于表中的已有键来说是唯一的，但它可能与之前从表中删除的键值重叠。要始终得到在整个表中唯一的键，在INTEGER PRIMARY KEY的声明之前加关键词AUTOINCREMENT.这样被选的键将总是比表中已存在的最大键大1。若可能的最大键已存在于表中，INSERT操作将失败并返回一个SQLITE_FULL错误码.</p>
<hr />
<p>在SQLite中，每个表的每一行都有一个64位有<a href="http://www.sqlite.org/lang_createtable.html#rowid">符号整数</a>的 ROWID。每一行的ROWID是在同一个表的所有行中是独一无二 的。</p>
<p>您可以访问一个SQLite使用一个特殊的列名的ROWID，_ROWID_，或OID的表的ROWID。除非你声明一个普通的表列使用这些特殊的名字，然后使用该名称将参照声明的列而不是内部的ROWID。</p>
<p>&ldquo;如果一个表包含<a href="http://www.sqlite.org/lang_createtable.html#rowid">一个INTEGER PRIMARY</a> KEY类型的列，那么该栏将变为的ROWID的别名 。然后，您可以访问ROWID的使用四个不同的名字，原来的三个名字上面描述的姓名或名称<a href="http://www.sqlite.org/lang_createtable.html#rowid">INTEGER PRIMARY KEY</a>列。所有这些名字都是彼此的别名，在任何情况下，同样的 工作。</p>
<p>当一个新行插入到一个SQLite表的ROWID可以被指定为INSERT语句的一部分，也可以由数据库引擎自动分配。要手动指定一个ROWID，它包括在要插入的值列表。例如：</p>
<blockquote> 创建表test1（一个int，B文本）;
  <pre class="prettyprint linenums">INSERT INTO TEST1（ROWID，A，B）VALUES（123，5，'你好'）;  </pre>
</blockquote>
<p>如果没有插入指定的ROWID，或者如果指定的ROWID有一个NULL值，然后一个适当的ROWID是自动创建的 。通常的算法是给新创建的行的ROWID，它是一个比前插入表中的最大的 ROWID。如果表最初是空的，那么1 ROWID的使用。如果最大的ROWID是等于最大可能的整数（9223372036854775807），然后启动数据库引擎随意采摘的积极候选人的ROWIDs，直到找到一个以前没有使用过。如果没有未使用的ROWID可以找到合理的尝试后，插入操作失败<a href="http://www.sqlite.org/c3ref/c_abort.html">与</a> SQLITE_FULL错误 。如果没有负面的ROWID值显式插入，然后自动生成的ROWID值将始终 大于零。</p>
<p>以上所述的正常ROWID选择算法会产生单调增加的，只要独特的ROWIDs，因为你从来没有使用的最大的ROWID值，你永远不删除表中的条目具有最大rowid的。如果你删除行或如果你曾创建的最大可能的ROWID一排，然后从以前删除的行的ROWIDs创建新行和新创建的ROWIDs可能没有严格按升序时，可能会被重用。</p>
<h2>自动增量关键字</h2>
<p>如果列类型的INTEGER PRIMARY KEY AUTOINCREMENT，则略有不同的ROWID选择算法用于。新行选择的rowid是至少有一个较大的比以往任何时候都在同一个表中存在的最大的ROWID。，如果表中以前从未包含的所有数据，然后是1的ROWID使用。如果表中有先前举行的最大可能的ROWID一排，然后新的插入不允许任何试图插入一个新行会失败SQLITE_FULL错误。</p>
<p>SQLite的不断追踪最大的ROWID表一次使用特殊SQLITE_SEQUENCE表。SQLITE_SEQUENCE表是正常的表包含自动增量列创建时自动创建并初始化。可以修改的SQLITE_SEQUENCE表的内容，使用普通的UPDATE，INSERT和DELETE语句。但此表进行修改，可能会干扰AUTOINCREMENT的密钥生成算法。确保你知道你在做什么之前进行这种变化。</p>
<p>AUTOINCREMENT关键字所实施的行为是巧妙地从不同的默认行为。随着AUTOINCREMENT的，行与自动选择的ROWIDs保证有从未有过相同的表使用前在同一数据库中的ROWIDs。而自动生成的ROWIDs保证是单调递增的。这些都是在特定应用中的重要属性。但如果您的应用程序并不需要这些属性，你也许应该留在默认行为以来AUTOINCREMENT的使用需要额外的工作要做，每行插入，从而导致插入运行慢一点。</p>
<p>请注意，&ldquo;单调递增&rdquo;并不意味着永远只有一个增加的ROWID。一个是通常的增量。然而，如果插入失败，因为（举例来说）唯一性约束，插入尝试失败的ROWID可能不被重用在随后的插入，导致在ROWID序列的空白。AUTOINCREMENT的保证，自动选择的ROWIDs将增加，但并不是说他们将是连续的。</p>
</body>
</html>
